package cn.tedu.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

/**
 * Author：程Sir
 * Name： SmallServer
 * Content:
 * HttpRequest，表示请求对象。
 * 在这个类上我们将请求的相关信息设计为属性来分别保存。并在构造方法中完成解析请求的工作
 *
 * 因为ReuqestHandler类只需要做业务逻辑的控制，而具体的请求拆分我们需要单独封装到HtttpRequest类中完成。
 * 后期需要哪部分内容，直接调用即可。实现了功能的拓展
 *
 *
 */
public class HttpRequest {
    private Socket socket;
    /*
        请求行相关的信息
     */
    private String method; // 表示请求方式
    private String url;    // 表示抽象路径
    private String protocol;// 表示协议版本

    /**
     * 获取抽象路径的方法
     * @return 返回抽象路径
     */
    public String getUrl() {
        return url;
    }

    /*
            消息头相关的信息
    */
    Map<String, String > map = new HashMap<>();

    /*
        消息正文相关的信息
        TODO
     */

    public HttpRequest(Socket socket) throws EmptyRequestException {
        this.socket = socket;
        // 1.解析请求行
        parseRequestLine();
        // 2.解析消息头
        parseRequestHeaders();
        // 3.解析消息正文 = 当请求方式是post请求时，就需要对请求正文进行解析

        parseContent();

    }

    /**
     * 解析请求正文
     */
    private void parseContent() {

        System.out.println("开始解析请求正文....");
        // 判断是否是post请求
        if ("post".equalsIgnoreCase(method)){ // 忽略大小写
            // 判断是否携带数据
            String length = map.get("Content-Length");
            if (length != null){
                int len = Integer.valueOf(length);
                byte[] data = new byte[len];
                // 读取正文中的数据
                InputStream inputStream = null;
                try {
                    inputStream = socket.getInputStream();
                    inputStream.read(data);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // 如何判断提交的数据格式？
                String type = map.get("Content-Type");
                if (type !=null){
                    if ("application/x-www-form-urlencoded".equalsIgnoreCase(type)) {
                        try {
                            String line = new String(data, "ISO8859-1");
                            //解码为中文可视的
                            line = URLDecoder.decode(line, "utf-8");
                            // 解析请求参数
                            parseParameter(line);
                            System.out.println("拿到的请求正文是："+line);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }

        System.out.println("请求正文解析完毕！");

    }


    /**
     * 解析请求行
     */
    public void parseRequestLine() throws EmptyRequestException {
        // 读第一行字符串：请求行
        String line = readLine();
        // 如果是空请求，请求行应该是空的
        if (line.isEmpty()){
            throw new EmptyRequestException("浏览器发送了一个空请求！");
        }
        // 1. 解析请求【问】
        // 对请求行进行拆分，找出请求资源的路径
        String[] data = line.split("\\s");
        // 请求方式
        method = data[0];
        // 抽象路径
        url = data[1];
        // 版本信息
        protocol = data[2];
        System.out.println("拆分之后的结果：");
        System.out.println("请求方式："+method);
        System.out.println("请求路径："+url);
        System.out.println("协议版本："+protocol);
        parseUrl();
    }

    // 用来装所有客户端发送过来的参数和参数值的集合
    private Map<String, String> parameters = new HashMap<>();

    /**
     * 通过指定的key，获取对应的value的方法
     * @param name 指定的key
     * @return 返回对应的value，不存在返回null
     */
    public String getParameter(String name){
        return parameters.get(name);
    }
    private String requestURL; // ？拆分后的前面内容
    private String queryString;// ？拆分后的后面内容

    public String getRequestURL() {
        return requestURL;
    }

    /**
     * 解析URL路径
     */
    private void parseUrl(){
        /*
            现将拿到的字符串进行中文格式化、
            最先拿到的是ISO9950-1编码的
         */

        try {
            url = URLDecoder.decode(url,"utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        String[] datas = url.split("\\?");
        // 判断拆分之后的元素是不是大于1
        if(datas.length > 1){
            requestURL = datas[0];  // /web/indexhtml
            queryString = datas[1]; // username=zhangsan&password=123&age=18&....
            // 解析请求参数
            parseParameter(queryString);
        }else {
            requestURL = url;
        }
        System.out.println("requestURL: "+requestURL);
        System.out.println("queryString: "+queryString);
        System.out.println("解析完url后得到的请求参数："+parameters);
    }

    /**
     * 解析请求参数
     * @param queryString 需要被解析字符串 样式符合 username=zhangsan&password=123&age=18&....
     */
    private void parseParameter(String queryString) {
        String[] datas;// 解析参数
        // 对queryString按 & 进行拆分
        datas = queryString.split("&");
            /*
                通过遍历拿到的元素是：
                username=zhangsan
                password=123
                age=18
                ...
             */
        for (int i=0; i<datas.length; i++) {
        /*
            拿到的元素又可以按=拆分
            key       value

            username  zhangsan
            password  123
            age       18
            ...
         */
            String[] strs = datas[i].split("=");
            if (strs.length > 1) {
//                    String key = strs[0];
//                    String value = strs[1];
                parameters.put(strs[0], strs[1]);
            } else {
                parameters.put(strs[0], null);
            }
        }
    }


    public void parseRequestHeaders(){
        String line = null;
        /*
            读取消息头的信息
            key： value[CRLF]
         */

        while (true){
            line = readLine();
            // 因为消息头内容的结束是以单独发生一个连续的字符作为标识的
            if (line.isEmpty()){
                break;
            }
//            System.out.println("读到的消息头是："+line);

            // 按照：冒号进行拆分
            String[] data = line.split(":\\s");
            // 将消息头的名称作为map的key，将消息头的值作为map的value进行存在
            map.put(data[0],data[1]);
        }

        System.out.println("拆分之后map集合中的数据有：" +map);
    }

    /**
     * 读取一行字符串
     *
     * @return 返回读取到的一行字符串
     */
    private String readLine() {
        try {
            // 获取输入流
            InputStream inputStream = socket.getInputStream();

            // 声明两个标识符
            char pre = ' '; // 已拼接的字符
            char cur = ' '; // 当前读到字符
            // 声明一个空字符串，用来装读到的字符
            StringBuilder builder = new StringBuilder();
            int a ;
            while ((a=inputStream.read())!=-1){
                char b = (char) a; //  将int值转换为char
                // 将读到的字符装入当前字符中，目的是进行判断
                cur = b;
                if ( pre ==13 && cur ==10){ // 字符串末尾的判断
                    break;
                }
                builder.append(cur);
                // 将读到字符拼接完成后，标识为已拼接的zif
                pre = cur;
            }
//            System.out.println("读到的一行内容是："+builder);
            return builder.toString().trim(); // 因为空白符CR（13）已经被拼接进去了，需要去除空白符
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
